package com.wangtianxiang.rabbitmq.resolve;

import com.alibaba.fastjson.JSONObject;
import com.wangtianxiang.rabbitmq.utils.JSONObjectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.env.OriginTrackedMapPropertySource;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.EnumerablePropertySource;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;

import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;

public class PropertiesResolve {
	private static final  Logger logger = LoggerFactory.getLogger(PropertiesResolve.class);
	
	/**
	 * merge properties
	 * 
	 * @param propertySource
	 *            properties source.
	 * @param properties
	 *            the properies that exist, can be null.
	 * @param configPrefix
	 *            the properties name you want to limit
	 * @return
	 */
	private static Properties mergeProperties(PropertySource<?> propertySource, final Properties properties,
			String configPrefix) {
		EnumerablePropertySource enumProp = null;
		if (null == properties) {
			throw new IllegalArgumentException("'properties' must not be null");
		}
		if (null == propertySource) {
			return properties;
		}
		// CompositePropertySource
		if (propertySource instanceof CompositePropertySource || propertySource instanceof OriginTrackedMapPropertySource) {
			enumProp = (EnumerablePropertySource) propertySource;
			String[] propertiesNames = enumProp.getPropertyNames();
			String keyTmp = null;
			for (String name : propertiesNames) {
				// filter config prifix if 'configPrefix' is not empty.
				keyTmp = name;
				if (null != configPrefix && !configPrefix.isEmpty()) {
					if (!name.startsWith(configPrefix)) {
						continue;
					}
					keyTmp  = name.substring(configPrefix.length() + 1).replaceAll("-", "").toLowerCase();
				}
				properties.put(keyTmp, enumProp.getProperty(name));
			}
		}else {
			// default logic
			Object source = propertySource.getSource();
			if (source instanceof Properties) {
				final Properties prop = (Properties) source;
				prop.keySet().stream().map(mapper -> {
					return mapper + "";
				}).filter(value -> {
					return ("" + value).startsWith(configPrefix);
				}).forEach(key -> {
					properties.put(key.substring(configPrefix.length() + 1).replaceAll("-", "").toLowerCase(),
							prop.get(key));
				});
			}else if(source instanceof Map){
				final Map<String,Object> prop = (Map) source;
				prop.keySet().stream().map(mapper -> {
					return mapper + "";
				}).filter(value -> {
					return ("" + value).startsWith(configPrefix);
				}).forEach(key -> {
					properties.put(key.substring(configPrefix.length() + 1).replaceAll("-", "").toLowerCase(),
							prop.get(key));
				});
			}
		}

		return properties;

	}

	public static <T> T resolve(MutablePropertySources propertyResolver, String configPrefix, Class<T> c) {

		if (null != propertyResolver) {
			// Properties
			final Map<String, Object> propertyMap = new TreeMap<String, Object>();
			Properties properties = new Properties();
			for (PropertySource<?> propertySource : propertyResolver) {
				logger.info(
						"scan config params, propertySource name :{},propertySource source's type:{}, all configNames:{}",
						propertySource.getName(), propertySource.getSource().getClass());
				mergeProperties(propertySource, properties, configPrefix);

			}
			properties.entrySet().forEach(entry -> {
				propertyMap.put("" + entry.getKey(), entry.getValue());
			});

			// format
			final JSONObject property = new JSONObject();
			propertyMap.forEach((key, value) -> {
				// format config key as json style
				int deep = key.replaceAll("[^(\\.)]", "").length();
				String tempV = value == null ? "" : value + "";
				String before = "{\"" + key.replaceAll("\\.", "\":{\"") + "\":";
				tempV = tempV.replaceAll("\\\\", "\\\\\\\\");
				tempV = tempV.replaceAll("\"", "\\\\\"");
				String after = "";
				while (after.length() <= deep) {
					after += "}";
				}
				String json = before + "\"" + tempV + "\"" + after;
				property.putAll(JSONObjectUtil.merge(JSONObject.parseObject(json, JSONObject.class), property));
			});
			if (logger.isTraceEnabled()) {
				logger.trace("Could not find xx in any property source");
			}
			return JSONObject.toJavaObject(property, c);
		}
		return null;
	}
	


}
